package com.robaone.html;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;

import org.htmlcleaner.CleanerProperties;
import org.htmlcleaner.ContentToken;
import org.htmlcleaner.HtmlCleaner;
import org.htmlcleaner.PrettyXmlSerializer;
import org.htmlcleaner.TagNode;

import com.robaone.util.Strmanip;

public class WidgetGenerator2 {
	HashMap<String,Object> statevariables = new HashMap<String,Object>();
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		WidgetGenerator2 gen = new WidgetGenerator2();
		try{
			gen.processArguments(args);
			String code = gen.generateClass(gen.getHTMLSRC(),gen.m_classname,gen.m_package);
			gen.saveCode(code);
		}catch(Exception e){
			System.out.println("Error: "+e.getMessage());
			gen.showUsage();
			e.printStackTrace();
		}
	}

	private void saveCode(String class_src) throws Exception {
		System.out.println("\n=============== The Class Source ===============");
		System.out.println("package "+this.m_package+";\n");
		System.out.println(class_src);

		class_src = "package "+this.m_package+";\n" + class_src;
		this.m_destination_folder.mkdirs();
		File output = new File(this.m_destination_folder.getAbsolutePath()+System.getProperty("file.separator")+this.m_classname+".java");
		FileWriter fw = new FileWriter(output);
		fw.write(class_src);
		fw.close();
	}
	public String cleanHTML(String html) throws Exception{
		HtmlCleaner cleaner = new HtmlCleaner();
		CleanerProperties props = cleaner.getProperties();
		props.setUseCdataForScriptAndStyle(true);
		props.setRecognizeUnicodeChars(true);
		props.setUseEmptyElementTags(true);
		props.setAdvancedXmlEscape(true);
		props.setTranslateSpecialEntities(true);
		props.setBooleanAttributeValues("empty");
		TagNode node = cleaner.clean(html);
		ByteArrayOutputStream bout = new ByteArrayOutputStream();
		
		new PrettyXmlSerializer(props).writeXmlToStream(node, bout);
		String retval = bout.toString();
		return retval;
	}
	public String generateClass(String html, String mClassname,
			String mPackage) throws Exception {
		this.setClassName(mClassname);
		this.setPackage(mPackage);
		HtmlCleaner cleaner = new HtmlCleaner();
		CleanerProperties props = cleaner.getProperties();
		props.setUseCdataForScriptAndStyle(true);
		props.setRecognizeUnicodeChars(true);
		props.setUseEmptyElementTags(true);
		props.setAdvancedXmlEscape(true);
		props.setTranslateSpecialEntities(true);
		props.setBooleanAttributeValues("empty");
		TagNode node = cleaner.clean(html);
		this.setNamespace(node);
		int level = 0;
		TagNode[] tags = node.getChildTags();
		for(int i = 0; i < tags.length;i++){
			String tagname = tags[i].getName();
			System.out.println("Tag = "+tagname);
			if(tagname.equals("body")){
				try{
					String mypackage = tags[i].getAttributeByName(this.getNamespace()+":package");
					if(mypackage != null){
						this.m_package = mypackage;
					}
				}catch(Exception e){}
				try{
					String classname = tags[i].getAttributeByName(this.getNamespace()+":class");
					if(classname != null){
						this.setClassName(classname);
					}
				}catch(Exception e){}
				WidgetDescription compositeui = this.generateWidgets(tags[i],level+1,null);
				String class_src = compositeui.buildClass(this.m_classname);
				
				class_src = "package "+this.m_package+";\n" + class_src;
				return class_src;
			}
		}
		return null;
	}

	private String m_package;
	private File m_destination_folder;
	private String m_classname;
	private File m_source_file;
	private int m_itemcounter;
	private static String m_namespace;

	private void processArguments(String[] args) throws Exception {
		if(args.length < 4){
			throw new Exception("Not enough arguments.");
		}
		this.setSourceFile(args[0]);
		this.setClassName(args[1]);
		this.setPackage(args[2]);
		this.setDestinationFolder(args[3]);
	}

	private void setSourceFile(String string) {
		File source = new File(string);
		this.m_source_file = source;
	}

	private void setClassName(String string) {
		this.m_classname = string;
	}

	private void setPackage(String string) {
		this.m_package = string;
	}
	
	public static String getNamespace(){
		return m_namespace;
	}

	private void setDestinationFolder(String string) throws Exception {
		File file = new File(string + System.getProperty("file.separator") + Strmanip.replacechar(this.m_package, '.', System.getProperty("file.separator").charAt(0)));
		this.m_destination_folder = file;
	}

	private void generateClass() throws Exception {
		HtmlCleaner cleaner = new HtmlCleaner();
		CleanerProperties props = cleaner.getProperties();
		props.setUseCdataForScriptAndStyle(true);
		props.setRecognizeUnicodeChars(true);
		props.setUseEmptyElementTags(true);
		props.setAdvancedXmlEscape(true);
		props.setTranslateSpecialEntities(true);
		props.setBooleanAttributeValues("empty");
		String html = this.getHTMLSRC();
		TagNode node = cleaner.clean(html);
		this.setNamespace(node);
		int level = 0;
		TagNode[] tags = node.getChildTags();
		for(int i = 0; i < tags.length;i++){
			String tagname = tags[i].getName();
			System.out.println("Tag = "+tagname);
			if(tagname.equals("body")){
				WidgetDescription compositeui = this.generateWidgets(tags[i],level+1,null);
				String class_src = compositeui.buildClass(this.m_classname);
				System.out.println("\n=============== The Class Source ===============");
				System.out.println("package "+this.m_package+";\n");
				System.out.println(class_src);

				class_src = "package "+this.m_package+";\n" + class_src;
				this.m_destination_folder.mkdirs();
				File output = new File(this.m_destination_folder.getAbsolutePath()+System.getProperty("file.separator")+this.m_classname+".java");
				FileWriter fw = new FileWriter(output);
				fw.write(class_src);
				fw.close();
				break;
			}
		}
	}

	private void setNamespace(TagNode node) {
		if(node != null){
			String[] names = (String[])node.getAttributes().keySet().toArray(new String[0]);
			for(int i = 0; i < names.length;i++){
				String key = names[i];
				if(key.startsWith("xmlns")){
					String value = node.getAttributeByName(key);
					if(value.equals("http://www.robaone.com/gwt")){
						String[] tokens = key.split(":");
						String namespace = "";
						if(tokens.length > 0){
							namespace = tokens[1];
						}
						this.m_namespace = namespace;
						return;
					}
				}
			}
		}
	}

	private WidgetDescription generateWidgets(TagNode node,int level,WidgetDescription parent) throws Exception {
		TagNode[] tags = node.getChildTags();
		WidgetDescription root = null;
		if(parent == null){
			this.m_itemcounter = 0;
			this.padLine(level);
			System.out.println("Create the RootWidget ("+node.getName()+")");
			this.padLine(level);
			root = new WidgetDescription(node);
			root.setItemindex(this.m_itemcounter);
			this.m_itemcounter++;
		}else{
			this.padLine(level);
			System.out.println("Create Widget for "+node.getName());
			this.padLine(level);
			System.out.println("Attributes = "+node.getAttributes());
			this.padLine(level);
			root = new WidgetDescription(node);
			root.setItemindex(this.m_itemcounter);
			this.m_itemcounter++;
			if(parent != null){
				//parent.addChildWidget(root);
			}
			if(node.getName().equals("table")){
				HashMap<String,Object> temp = statevariables;
				statevariables = new HashMap<String,Object>();
				statevariables.put("state", "1");

				WidgetDescription table = this.processTable(root,level+1,node);
				table.setContent(statevariables);
				statevariables = temp;
				return table;
			}else if(node.getName().equals("select")){
				HashMap<String,Object> temp = statevariables;
				statevariables = new HashMap<String,Object>();
				WidgetDescription select = this.processSelect(root,level+1,node);
				select.setContent(statevariables);
				statevariables = temp;
				return select;
			}else if(node.getName().equals("form")){
				
			}
		}
		for(int i = 0; i < tags.length;i++){
			String tagname = tags[i].getName();
			this.padLine(level);
			System.out.println("Tag = "+tagname);
			WidgetDescription child = this.generateWidgets(tags[i], level+1,root);
			root.addChildWidget(child);
		}
		for(int i = 0;i < node.getChildren().size();i++){
			Object o = node.getChildren().get(i);
			if(o instanceof ContentToken){
				ContentToken child = (ContentToken)o;
				if(child.getContent() != null){
					if(child.getContent().trim().length() > 0){
						this.padLine(level);
						System.out.println("Add content ("+child.getContent().trim()+") to widget.");
						WidgetDescription text = new WidgetDescription(child);
						text.setContent(child.getContent().trim());
						root.addChildWidget(text);
					}
				}
			}
		}

		return root;
	}

	private WidgetDescription processSelect(WidgetDescription root,int level, TagNode node) throws Exception {
		return root;
	}

	private WidgetDescription processTable(WidgetDescription root,int level,TagNode node) throws Exception {
		int state = Integer.parseInt((String)statevariables.get("state"));
		String name = node.getName();
		switch(state){
		case 1:
			this.padLine(level);
			System.out.println("Generate FlexTable");
			for(int i = 0; i < node.getChildTags().length;i++){
				statevariables.put("state", "2");
				this.processTable(root,level+1, node.getChildTags()[i]);
			}
			break;
		case 2:
			if(name.equalsIgnoreCase("tbody")){
				statevariables.put("state", "3");
			}else if(name.equalsIgnoreCase("thead")){
				statevariables.put("state", "4");
			}
			for(int i = 0; i < node.getChildTags().length;i++){
				this.processTable(root,level+1, node.getChildTags()[i]);
			}
			break;
		case 3:

		case 4:
			if(name.equalsIgnoreCase("tr")){
				statevariables.put("columnnumber", "0");
				if(statevariables.get("rownumber") == null){
					statevariables.put("rownumber", "0");
				}else{
					int row = Integer.parseInt((String)statevariables.get("rownumber"));
					row++;
					statevariables.put("rownumber", ""+row);
				}
				String style = node.getAttributeByName("class");
				if(style != null){
					statevariables.put("rowstyle."+statevariables.get("rownumber"), style);
				}
				for(int i = 0; i < node.getChildTags().length;i++){
					statevariables.put("state", "5");
					this.processTable(root,level+1,node.getChildTags()[i]);
				}
			}else{
				statevariables.put("state", "2");
			}
			break;
		case 5:
			if(name.equalsIgnoreCase("td")){
				int column = Integer.parseInt((String)statevariables.get("columnnumber"));
				int row = Integer.parseInt((String)statevariables.get("rownumber"));
				statevariables.put("columnnumber",""+column);
				String style = node.getAttributeByName("class");
				if(style != null){
					statevariables.put("cellclass."+row+"."+column, style);
				}
				String attrib = node.getAttributeByName("style");
				if(attrib != null){
					statevariables.put("cellstyle."+row+"."+column, attrib);
				}
				String width = node.getAttributeByName("width");
				if(width != null){
					statevariables.put("cellwidth."+row+"."+column, width);
				}
				WidgetDescription cell = this.generateWidgets(node, level+1, root);
				statevariables.put("widget."+row+"."+column, cell);
				column = Integer.parseInt((String)statevariables.get("columnnumber"));
				column++;
				statevariables.put("columnnumber", ""+column);
				statevariables.put("state", "4");
				root.addChildWidget(cell);
			}

			break;

		}
		return root;
	}

	private void padLine(int level) {
		for(int index = 0; index < level;index++) System.out.print("  ");
	}

	private String getHTMLSRC() throws IOException {
		FileReader fw = new FileReader(this.m_source_file);
		StringBuffer buffer = new StringBuffer();
		char[] buf = new char[500];
		for(int i = fw.read(buf);i > -1;i = fw.read(buf)){
			buffer.append(buf, 0, i);
		}
		if(buffer.charAt(0) == 239){
			return buffer.toString().substring(3);
		}else{
			return buffer.toString();
		}
	}

	private void showUsage() {
		System.out.println("Usage: [source file] [name] [package] [destination]");
		System.out.println("  source file: The file name of the source html.");
		System.out.println("         name: The name of the class to be created.");
		System.out.println("      package: The package of the generated class.");
		System.out.println("  destination: The destination folder of the generated class.");
	}

}
